/*
 * usercustomer.h
 *
 *  Created on: Jun 16, 2011
 *      Author: peckb1
 */

#ifndef USERCUSTOMER_H_
#define USERCUSTOMER_H_

#include "syscall.h"
#include "userSharedDefinitions.h"
#include "usercommon.h"
#include "userItem.h"
#include "userLocks.h"
#include "userHomedepot.h"

typedef struct Customer {
    int _customerID;
    int _wantsToReturnItems;
    int _shoppingList[MAX_SHOPPING_LIST];
    int _shoppingListSize;

} Customer;

int PickupItem(int customerID, int aisleNumber, int currentItemIDToPickup) {
	int itemLock;
	int reStockLock;
	int couldPickupItem;
	#ifdef IGNORE_STOCKBOYS
        return(TRUE);
    #endif
    itemLock  = theLocksInHomeDepotEntry._shelfLock[currentItemIDToPickup];
    reStockLock = theLocksInHomeDepotEntry._customerWaitingForReStockLock;
    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        printf_7("CUSTOMER# "
        		,customerID
        		,": PickupItem(aisle# "
        		,aisleNumber
        		,", itemID# "
        		,currentItemIDToPickup
        		,") Started!\n");
    #endif
    #ifdef PRINT_LOCKS_STOCKBOY
        printf_5("CUSTOMER# "
        		,customerID
        		," itemLock["
        		,currentItemIDToPickup
        		,"]->Acquire()!\n");
    #endif

    Acquire(itemLock);

    #ifdef PRINT_LOCKS_STOCKBOY
        printf_5("CUSTOMER# "
        		,customerID
        		," itemLock["
        		,currentItemIDToPickup
        		,"]->Acquire() Done!\n");
    #endif
    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        printf_7("CUSTOMER# "
        		,customerID
        		,": PickupItem(aisle# "
        		,aisleNumber
        		,", itemID# "
        		,currentItemIDToPickup
        		,") Locked the item\n");
    #endif

    couldPickupItem = FALSE;

    do {
        couldPickupItem = PickupItemFromStore(currentItemIDToPickup);
        if (couldPickupItem==FALSE) {
            #ifdef PRINT_LOCKS_STOCKBOY
                printf_5("CUSTOMER# "
                		,customerID
                		," itemLock["
                		,currentItemIDToPickup
                		,"]->Release()!\n");
            #endif

            Release(itemLock);

            #ifdef PRINT_LOCKS_STOCKBOY
                printf_5("CUSTOMER# "
                		,customerID
                		," itemLock["
                		,currentItemIDToPickup
                		,"]->Release() Done!\n");
            #endif
            #ifdef PRINT_EXTRA_LOG_CUSTOMER
                printf_7("CUSTOMER# "
                		,customerID
                		,": PickupItem(aisle# "
                		,aisleNumber
                		,", itemID# "
                		,currentItemIDToPickup
                		,") Couldn't pickup item. Lets wait for a stock boy to re stock it.!\n");
            #endif
            #ifdef PRINT_LOCKS_STOCKBOY
                printf_3("CUSTOMER# "
                		,customerID
                		," reStockLock->Acquire()!\n");
            #endif

            Acquire(reStockLock);

            #ifdef PRINT_LOCKS_STOCKBOY
                printf_3("CUSTOMER# "
                		,customerID
                		," reStockLock->Acquire() Done!\n");
            #endif
            #ifdef PRINT_EXTRA_LOG_CUSTOMER
                printf_7("CUSTOMER# "
                		,customerID
                		,": PickupItem(aisle# "
                		,aisleNumber
                		,", itemID# "
                		,currentItemIDToPickup
                		,") lets get into the line of customers waiting for re stocking.!\n");
            #endif

            theLocksInHomeDepotEntry._customersWaitingForRestock[currentItemIDToPickup]++;

            /* lets wait for a stock boy to re stock this item */
            #ifdef PRINT_EXTRA_LOG_CUSTOMER
                printf_7("CUSTOMER# "
                		,customerID
                		,": PickupItem(aisle# "
                		,aisleNumber
                		,", itemID# "
                		,currentItemIDToPickup
                		,") lets wait on this line!\n");
            #endif
            #ifdef PRINT_LOCKS_STOCKBOY
                printf_5("CUSTOMER #"
                		,customerID
                		," itemCV[reStockLock]["
                		,currentItemIDToPickup
                		,"]->Wait()!\n");
            #endif

            Wait(theLocksInHomeDepotEntry._itemWaitCV[currentItemIDToPickup], reStockLock);

            /* We just got a signal from stock boy that item is available for pickup */
            #ifdef PRINT_LOCKS_STOCKBOY
                printf_5("CUSTOMER #"
                		,customerID
                		," itemCV[reStockLock]["
                		,currentItemIDToPickup
                		,"]->Wait() Done!\n");
            #endif
            #ifdef PRINT_EXTRA_LOG_CUSTOMER
                printf_7("CUSTOMER# "
                		,customerID
                		,": PickupItem(aisle# "
                		,aisleNumber
                		,", itemID# "
                		,currentItemIDToPickup
                		,") just got the signal from stock boy that item is available.\n");
            #endif
            #ifdef PRINT_LOCKS_STOCKBOY
                printf_5("CUSTOMER #"
                		,customerID
                		," itemLock["
                		,currentItemIDToPickup
                		,"]->Acquire()!\n");
            #endif

            Acquire(itemLock);

            #ifdef PRINT_LOCKS_STOCKBOY
                printf_5("CUSTOMER #"
                		,customerID
                		," itemLock["
                		,currentItemIDToPickup
                		,"]->Acquire() Done!\n");
            #endif
            #ifdef PRINT_LOCKS_STOCKBOY
                printf_3("CUSTOMER #"
                		,customerID
                		," restockLock->Release()!\n");
            #endif

            Release(reStockLock);

            #ifdef PRINT_LOCKS_STOCKBOY
                printf_3("CUSTOMER #"
                		,customerID
                		," reStockLock->Release() Done!\n");
            #endif
        }
    } while(couldPickupItem==FALSE);

    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        printf_7("CUSTOMER# "
        		,customerID
        		,": PickupItem(aisle# "
        		,aisleNumber
        		,", itemID# "
        		,currentItemIDToPickup
        		,") Successfully picked up the item!\n");
    #endif
    #ifdef PRINT_LOCKS_STOCKBOY
        printf_5("CUSTOMER# "
        		,customerID
        		," itemLock["
        		,currentItemIDToPickup
        		,"]->Release()!\n");
    #endif

    Release(itemLock);

    #ifdef PRINT_LOCKS_STOCKBOY
        printf_5("CUSTOMER# "
        	   ,customerID
        	   ," itemLock["
        	   ,currentItemIDToPickup
        	   ,"]->Release() Done!\n");
    #endif

    return(couldPickupItem);
}

int
LookForAisleNumber(int customerID, int itemId) {
    int aisleNumber;
	int chanceToFindTheAisle;
	int aisleThatWeFoundTheStockBoyIn;
	int currentAisleIndex;
	int currentStockBoyStatus;
	int customersWaiting;
	int customersWaitingCondition;
	int questionLock;
	int questionCondition;
	int answerLock;
	int answerCondition;
	int currentStockBoyIndex;
	#ifdef IGNORE_STOCKBOYS
        return theLocksInHomeDepotEntry.itemsInStore[itemId]._aisle;;
    #endif
    aisleNumber = NOT_FOUND;
    /* lets find out if we are lucky enough to know where the item is */
    chanceToFindTheAisle = Rand(100);
    /* 70% of times correctly can find the aisle number */
    if (chanceToFindTheAisle <= 75) {
        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            printf_3("CUSTOMER# ",customerID,": Can find the aisle ourself \n");
        #endif
        aisleNumber = theLocksInHomeDepotEntry.itemsInStore[itemId]._aisle;
    } else {
        #ifdef PRINT_OUTPUT_CUSTOMER
            printf_5("CUSTOMER# ",customerID,"] is not able to find item [",itemId,"] and is searching for StockBoy.\n");
        #endif
        /* 25% of times he/she does not know the aisleNumber. */
        /* lets look in aisles to find a stock boy and ask him the aisle number. */
        /* first we need to lock statuses of stockBoys */
        Acquire(theLocksInHomeDepotEntry._stockBoysStatusLock);
        aisleThatWeFoundTheStockBoyIn = NOT_FOUND;
        /* we have to go through aisles one by one to find a stock boy */
        for(currentAisleIndex=0; currentAisleIndex < MAX_AISLES; currentAisleIndex++) {
            /* Lets see if any stock boy is in current aisle */
            for(currentStockBoyIndex=0; currentStockBoyIndex < MAX_STOCKBOYS; currentStockBoyIndex++) {
                currentStockBoyStatus = theLocksInHomeDepotEntry._stockBoysStatus[currentStockBoyIndex];
                if (currentStockBoyStatus == currentAisleIndex) {
                    aisleThatWeFoundTheStockBoyIn = currentAisleIndex;
                    break;
                }
            }
            if (aisleThatWeFoundTheStockBoyIn != NOT_FOUND) {
                break;
            }
        }

        if (aisleThatWeFoundTheStockBoyIn == NOT_FOUND) {
            /* Couldn't find any stock boys in any aisle, so we should form a line */
            /* in service area */
            #ifdef PRINT_EXTRA_LOG_CUSTOMER
                printf_5("CUSTOMER# ",customerID,": LookForAisleNumber(itemID# ",itemId ,") Couldn't find any stock boy in aisles! SERVICE BUTTUN RINGED and wait for stock boy!\n");
            #endif
            #ifdef PRINT_OUTPUT_CUSTOMER
                printf_3("Customer [",customerID, "] looked in all aisle for StockBoy. He is now requesting help by pressing service button.\n");
            #endif
            aisleThatWeFoundTheStockBoyIn = SERVICE_AREA_LINE; /* SERVICE_AREA_LINE = MAX_AISLES = last index of the array */
        }
        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            else {
                printf_5("CUSTOMER# ",customerID,": Waiting for a stock boy in aisle# "
                		 ,aisleThatWeFoundTheStockBoyIn," \n");
            }
        #endif

        customersWaiting = theLocksInHomeDepotEntry._customersWaitingInAisleToAskQuestionLock[aisleThatWeFoundTheStockBoyIn];
        customersWaitingCondition = theLocksInHomeDepotEntry._customersWaitingInAisleToAskQuestionCV[aisleThatWeFoundTheStockBoyIn];
        Release(theLocksInHomeDepotEntry._stockBoysStatusLock);
        Acquire(customersWaiting);
        /* Please notice that the following function returns a pointer so we can easily increment its content */
        theLocksInHomeDepotEntry._customersWaitingInAisleToAskQuestionMV[aisleThatWeFoundTheStockBoyIn]++;
        /* Lets wait for stock boy to finish his task. */
        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            printf_5("CUSTOMER# ",customerID,": LookForAisleNumber(itemID# ",
            			itemId,") Waiting for a stock boy to help us\n");
        #endif

        Wait(customersWaitingCondition, customersWaiting);

        #ifdef PRINT_EXTRA_LOG_CUSTOMER
                printf_5("CUSTOMER# "
                	   , customerID
                	   ,": LookForAisleNumber(itemID# "
                	   ,itemId
                	   ,") just got woken up by a stock boy\n");
        #endif

        /* a stock boy just woke us up to answer our question. We need to search for that stock boy. */
        /* as convention, once a stock boy wakes a customer up, he needs to add MAX_AISLES to his */
        /* status so the customer can find him. i.e. now we need to search for a stock boy */
        /* with his status set to (aisleThatWeFoundTheStockBoyIn+NUM_CUSTOMERS_WAIT_LINES_FOR_STOCKBOYS) */

        currentStockBoyIndex  = theLocksInHomeDepotEntry._stockBoyIdInAisleToAnswerQuestionMV[aisleThatWeFoundTheStockBoyIn];
        /* ASSERT (currentStockBoyIndex < MAX_STOCKBOYS); / * We assume we must find a stockboy ready for help * / */

        questionLock = theLocksInHomeDepotEntry._customerItemQuestionLock[currentStockBoyIndex];
        answerLock = theLocksInHomeDepotEntry._stockBoyAisleAnswerLock[currentStockBoyIndex];
        questionCondition = theLocksInHomeDepotEntry._customerItemQuestionCV[currentStockBoyIndex];
        answerCondition = theLocksInHomeDepotEntry._stockBoyAisleAnswerCV[currentStockBoyIndex];

        #ifdef PRINT_OUTPUT_CUSTOMER
            printf_5("CUSTOMER# "
            		,customerID
            		,"] is asking for assistance from StockBoy ["
            		,currentStockBoyIndex
            		,"]\n");
        #endif

        Release(customersWaiting);
        Acquire(questionLock);

        /* currentStockBoyIndex is who we can ask our question from. */
        /* we need to ask our question through locking a shared monitor variable. */
        /* This needs to happen before we release the customersWaitingInAisleToAskQuestionLock, because */
        /* we need to make other customers wait till our question is being answered. */
        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            printf_5("CUSTOMER# "
            		,customerID
            		," locked the question for Stockboy "
            		,currentStockBoyIndex
            		,"\n");
        #endif

        theLocksInHomeDepotEntry._customerItemQuestionMV[currentStockBoyIndex] = itemId;
        theLocksInHomeDepotEntry._customerID_AskingQuestionMV[currentStockBoyIndex] = customerID;
        Signal(questionCondition, questionLock);
        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            printf_7("CUSTOMER# "
            		,customerID
            		," put the item#"
            		,itemId
            		," in the question place for Stockboy "
            		,currentStockBoyIndex
            		,"\n");
        #endif

        /* We asked our question, now we need to wait till stock boy receives the answer, and hands over the aisle number */
        Acquire(answerLock);

        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            printf_5("CUSTOMER# "
            		,customerID
            		," acquired the answer lock for Stockboy "
            		,currentStockBoyIndex
            		,"\n");
        #endif

        Release(questionLock);

        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            printf_5("CUSTOMER# "
            		,customerID
            		," waiting for answer from Stockboy "
            		,currentStockBoyIndex
            		,"\n");
        #endif

        Wait(answerCondition, answerLock);

        /* Stock boy just provided the aisle number, lets grab it */
        aisleNumber = theLocksInHomeDepotEntry._stockBoyAisleAnswerMV[currentStockBoyIndex];

        #ifdef PRINT_OUTPUT_CUSTOMER
            printf_7("Customer ["
            		,customerID
            		,"] got aisle ["
            		,aisleNumber
            		,"] from StockBoy ["
            		,currentStockBoyIndex
            		,"\n");
        #endif

        Release(answerLock);
    }

    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        printf_7("CUSTOMER# "
        		,customerID
        		,": LookForAisleNumber(itemID# "
        		,itemId
        		,") the aisle# is "
        		,aisleNumber
        		,"\n");
    #endif

    return(aisleNumber);
}

void
CollectShoppingListItems(int _customerID,int _wantsToReturnItems,int* _shoppingList,int _shoppingListSize) {
	int shoppingListIndex;
	int currentItemIDToPickup;
	int aisleNumber;
	int couldPickupItem;
    for (shoppingListIndex = 0; shoppingListIndex < _shoppingListSize; shoppingListIndex++) {
        currentItemIDToPickup = _shoppingList[shoppingListIndex];

        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            printf_7("CUSTOMER# ",_customerID,": Collecting item index# ",shoppingListIndex," Num# ",currentItemIDToPickup, " \n");
        #endif
        aisleNumber = LookForAisleNumber(_customerID, currentItemIDToPickup);
        /* by now we know what is our aisleNumber */
        /* go to the aisle (this can be simply a tele-port to that aisle) */
        /*ASSERT (aisleNumber >=0 && aisleNumber < MAX_AISLES);*/
        /*aisleToSearch = _store->aisles[aisleNumber];*/
        #ifndef IGNORE_STOCKBOYS
            couldPickupItem = FALSE;
            while ( couldPickupItem==FALSE) {
            	couldPickupItem = PickupItem(_customerID, aisleNumber, currentItemIDToPickup);
            }
        #endif
    }
}

int
LookForShortestClerkLine(int customerID) {
    /* The locking is happening outside of this function */
	int i;
	int shortestLineLength = MAX_INT;
    int shortestLineIndex = NOT_FOUND;
    int anyClerkOnBreak = FALSE;
    int clerksOnBreak = 0;
    for(i = 0; i< MAX_CLERKS; i++) {
        if (theLocksInHomeDepotEntry._clerkStatus[i] == CLERK_BREAK) {
            clerksOnBreak++;
            anyClerkOnBreak = TRUE;
        } else if (theLocksInHomeDepotEntry._clerkLineLength[i] < shortestLineLength) {
            /* then clerk isn't on break */
            shortestLineLength = theLocksInHomeDepotEntry._clerkLineLength[i];
            shortestLineIndex = i;
        }
    }
    if (shortestLineLength > 5 && anyClerkOnBreak == TRUE) {
        /* lets not jump into this line but lets wait for a new clerk to come */
        shortestLineIndex = NOT_FOUND;
    }

    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        if (shortestLineIndex != NOT_FOUND) {
            printf_9("CUSTOMER# "
            		,customerID
            		,": LookForShortestClerkLine() the shortest line is "
            		,shortestLineIndex
            		," with "
            		,shortestLineLength
            		," customers in it, while there are "
            		,clerksOnBreak
            		," clerks on break.\n");
        }
    #endif

    return(shortestLineIndex);
}

int
GetIntoTheShortestClerkLine(int customerID) {
	int clerkLock;
	int clerkLineLock;
	int waitForClerksToReturn;
	int shortestLineIndex;
    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        printf_3("CUSTOMER# "
        		,customerID
        		,": GetIntoTheShortestClerkLine() lets find the shortest line\n");
    #endif

    clerkLineLock = theLocksInHomeDepotEntry._clerkLinelock;
    waitForClerksToReturn = theLocksInHomeDepotEntry._customerNeedsClerkAssitanceLock;

    #ifdef PRINT_LOCKS_CLERK
        printf_3("CUSTOMER# "
        		,customerID
        		," clerkLineLock->Acquire()\n");
    #endif

    Acquire(clerkLineLock);

    #ifdef PRINT_LOCKS_CLERK
        printf_3("CUSTOMER# "
        		,customerID
        		," clerkLineLock->Acquire() Done!\n");
    #endif

    shortestLineIndex = LookForShortestClerkLine(customerID);

    /* We need to notify the manager if all of the clerks are on break. */
    if (shortestLineIndex == NOT_FOUND)  {
        while (shortestLineIndex == NOT_FOUND) {

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CUSTOMER# "
                		,customerID
                		," clerkLineLock->Release()\n");
            #endif

            Release(clerkLineLock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CUSTOMER# "
                		,customerID
                		," clerkLineLock->Release() Done!\n");
            #endif
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CUSTOMER# "
                		,customerID
                		," waitForClerkToReturn->Acquire()\n");
            #endif

            Acquire(waitForClerksToReturn);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CUSTOMER# "
                		,customerID
                		," waitForClerkToReturn->Acquire() Done!\n");
            #endif
            #ifdef PRINT_EXTRA_LOG_CUSTOMER
                printf_3("CUSTOMER# "
                		,customerID
                		,": GetIntoTheShortestClerkLine() all of the clerks are in break, ask manager to send one for us\n");
            #endif

            (theLocksInHomeDepotEntry._customersNeedingAsistanceMV)++;
            /* Wait for manager so he can tell a clerk to come back to work. */
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CUSTOMER# "
                		,customerID
                		," customerAssistance[clerkLineLock]->Wait()\n");
            #endif

            Wait(theLocksInHomeDepotEntry._customerNeedsClerkAssitanceCV, waitForClerksToReturn);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CUSTOMER# "
                		,customerID
                		," customerAssistance[clerkLineLock]->Wait() Done!\n");
            #endif
            #ifdef PRINT_EXTRA_LOG_CUSTOMER
                printf_3("CUSTOMER# "
                		,customerID
                		,": GetIntoTheShortestClerkLine() Just got woken up to get into line\n");
            #endif

            /* Now we can check again for shortest line */
            #ifdef PRINT_LOCKS_CLERK
                printf_3("CUSTOMER# "
                		,customerID
                		," clerkLineLock->Acquire()\n");
            #endif

            Acquire(clerkLineLock);

            #ifdef PRINT_LOCKS_CLERK
                printf_3("CUSTOMER# "
                		,customerID
                		," clerkLineLock->Acquire() Done!\n");
            #endif

            shortestLineIndex = theLocksInHomeDepotEntry._clerkIDSignalingToCustomer;
        }

        (theLocksInHomeDepotEntry._clerkLineLength[shortestLineIndex])++;

        #ifdef PRINT_LOCKS_CLERK
            printf_3("CUSTOMER# "
            		,customerID
            		," customerShouldFindClerkCV->Signal()\n");
        #endif

        Signal(theLocksInHomeDepotEntry._customerShouldFindTheClerkCV, waitForClerksToReturn);

        #ifdef PRINT_LOCKS_CLERK
            printf_3("CUSTOMER# "
            		,customerID
            		,"waitForClerksToReturn->Release()\n");
        #endif

        Release(waitForClerksToReturn);

        #ifdef PRINT_LOCKS_CLERK
            printf("CUSTOMER# ");
            printf_int(customerID);
            printf("waitForClerksToReturn->Release() Done!\n");
        #endif

    } else {
        #ifdef PRINT_OUTPUT_CUSTOMER
            printf_7("Customer ["
            		,customerID
					,"] chose Clerk["
					,shortestLineIndex
					,"] with line length of ["
					,theLocksInHomeDepotEntry._clerkLineLength[shortestLineIndex]
					,"]\n");
        #endif

        /* by now we have found the shortest line. lets stay on the line */
        (theLocksInHomeDepotEntry._clerkLineLength[shortestLineIndex])++;
    }
    #ifdef PRINT_LOCKS_CLERK
        printf("CUSTOMER# ");
        printf_int(customerID);
        printf(" clerkLineCV[");
        printf_int(shortestLineIndex);
        printf("][clerkLineLock]->Wait()\n");
    #endif

    Wait(theLocksInHomeDepotEntry._clerkLineCV[shortestLineIndex], clerkLineLock);

    #ifdef PRINT_LOCKS_CLERK
        printf("CUSTOMER# ");
        printf_int(customerID);
        printf(" clerkLineCV[");
        printf_int(shortestLineIndex);
        printf("][clerkLineLock]->Wait() Done!\n");
    #endif

    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        printf_5("CUSTOMER# "
				,customerID
				,": GetIntoTheShortestClerkLine() Finally! its my turn in line#"
				,shortestLineIndex
				,"\n");
    #endif

    /* At this point, we just woke up (its our turn) so */
    /* lets get out of the line and lock the clerk so no one else can interrupt us */
    #ifdef PRINT_LOCKS_CLERK
        printf("CUSTOMER# ");
        printf_int(customerID);
        printf(" clerkLineLock->Release()\n");
    #endif

    Release(clerkLineLock);      /* We can safely release this lock, because clerk will not */
                                 /* signal other customers till he is done with current customer */
    #ifdef PRINT_LOCKS_CLERK
        printf("CUSTOMER# ");
        printf_int(customerID);
        printf(" clerkLineLock->Release() Done\n");
    #endif

    #ifdef PRINT_LOCKS_CLERK
        printf("CUSTOMER# ");
        printf_int(customerID);
        printf(" clerkLock[");
        printf_int(shortestLineIndex);
        printf("]->Acquire()\n");
    #endif

    Acquire(theLocksInHomeDepotEntry._clerkLock[shortestLineIndex]);

    #ifdef PRINT_LOCKS_CLERK
        printf("CUSTOMER# ");
        printf_int(customerID);
        printf(" clerkLock[");
        printf_int(shortestLineIndex);
        printf("]->Acquire() Done!\n");
    #endif

    return(shortestLineIndex);
}

void
ProvideTheItemsToClerk(int _customerID,int _wantsToReturnItems,int* _shoppingList,int _shoppingListSize,int clerkID) {
	int clerkLock;
	int customerLock;
	int currentItemIDToPassToClerk;
	int shoppingListIndex;
    clerkLock = theLocksInHomeDepotEntry._clerkLock[clerkID];
    customerLock = theLocksInHomeDepotEntry._customerLock[clerkID];

    /*clerkLock has already been acquired. Clerk is in sleep and waiting for us to provide */
    /* the nature of transaction, customer ID, etc and then */
    /* provide our items one by one. */
    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        printf_5("CUSTOMER# "
				,_customerID
				,": ProvideTheItemsToClerk(clerk#"
				,clerkID
				,") started providing the items\n");
    #endif

    Acquire(theLocksInHomeDepotEntry._customerActionLock);
    theLocksInHomeDepotEntry._customerBeingHelped[clerkID] = _customerID;
    theLocksInHomeDepotEntry._customerWantsToScanItems[clerkID] = _wantsToReturnItems;
    Release(theLocksInHomeDepotEntry._customerActionLock);

    /* put the items in shared area and signal the clerk, wait for clerk's signal */
    /* so we can put the next item */
    for (shoppingListIndex = 0; shoppingListIndex < _shoppingListSize; shoppingListIndex++) {
        currentItemIDToPassToClerk = _shoppingList[shoppingListIndex];
        theLocksInHomeDepotEntry._nextItem[clerkID] = _shoppingList[shoppingListIndex];

        #ifdef PRINT_OUTPUT_CUSTOMER
                printf_7("Customer ["
						,_customerID
						,"] gives item ["
						,_shoppingList[shoppingListIndex]
						,"] to Clerk["
						,clerkID
						,"]\n");
        #endif

        Signal(theLocksInHomeDepotEntry._clerkCV[clerkID], clerkLock);
        Wait(theLocksInHomeDepotEntry._customerCV[clerkID], clerkLock);

        #ifdef PRINT_EXTRA_LOG_CUSTOMER
            printf_5("CUSTOMER# "
					,_customerID
					,": ProvideTheItemsToClerk(clerk#"
					,clerkID
					,") Clerk said he is ready to take the next item\n");
        #endif
    }

    #ifdef PRINT_EXTRA_LOG_CUSTOMER
        printf_5("CUSTOMER# "
				,_customerID
				,": ProvideTheItemsToClerk(clerk#"
				,clerkID
				,") Let the clerk know we are done passing items\n");
    #endif
    #ifdef PRINT_OUTPUT_CUSTOMER
        printf_7("Customer ["
        ,_customerID
        ,"] tell Clerk ["
        ,clerkID
        ,"], I have given you all my ["
        ,_shoppingListSize
        ,"] items.\n");
    #endif

    theLocksInHomeDepotEntry._nextItem[clerkID] = NOT_FOUND; /* No more items to scan */
    Signal(theLocksInHomeDepotEntry._clerkCV[clerkID], clerkLock);
    Wait(theLocksInHomeDepotEntry._customerCV[clerkID], clerkLock);
}

void
TransferPayment(int _customerID,int _wantsToReturnItems,int* _shoppingList,int _shoppingListSize, int clerkID) {
    /* create local references to shared Locks/CV/MV */
	int clerkLock;
	int total;
    clerkLock = theLocksInHomeDepotEntry._clerkLock[clerkID];
    total = theLocksInHomeDepotEntry._moneyToTransfer[clerkID];

    #ifdef PRINT_OUTPUT_CUSTOMER
        if (_wantsToReturnItems==TRUE) {
            printf_7("Customer ["
					,_customerID
					,"] receives ["
					,total
					,"] from Clerk "
					,clerkID
					," and is now waiting for receipt.\n");
        } else {
            printf_7("Customer ["
					,_customerID
					,"] pays ["
					,total
					,"] to Clerk "
					,clerkID
					," and is now waiting for receipt.\n");
        }
    #endif

    Signal(theLocksInHomeDepotEntry._clerkCV[clerkID], clerkLock);

    /* Wait till clerk gives the receipt */
    #ifdef PRINT_EXTRA_LOG_CUSTOMER
    	if (_wantsToReturnItems)
    	{
			printf_5("CUSTOMER# "
					,_customerID
					,", Returning: TransferPayment(clerk#"
					,clerkID
					,") Wait for receipt.\n");
    	}
    	else
    	{
    		printf_5("CUSTOMER# "
					,_customerID
					,", Purchasing: TransferPayment(clerk#"
					,clerkID
					,") Wait for receipt.\n");
    	}
    #endif

    Wait(theLocksInHomeDepotEntry._customerCV[clerkID], clerkLock);

    #ifdef PRINT_OUTPUT_CUSTOMER
        printf_5("Customer ["
        		,_customerID
        		,"] got receipt from Clerk "
        		,clerkID
        		," and is now leaving.\n");
    #endif

    /* Let the clerk know we pickup our receipt */
    Signal(theLocksInHomeDepotEntry._clerkCV[clerkID], clerkLock);

    #ifdef PRINT_EXTRA_LOG_CUSTOMER
    	if (_wantsToReturnItems)
    	{
			printf_5("CUSTOMER# "
					,_customerID
					,", Returning: TransferPayment(clerk#"
					,clerkID
					,") Let the clerk know we got the receipt.\n");
    	}
    	else
    	{
    		printf_5("CUSTOMER# "
					,_customerID
					,", Purchasing: TransferPayment(clerk#"
					,clerkID
					,") Let the clerk know we got the receipt.\n");

    	}
    #endif
}


void
DoShopping(int _customerID, int _wantsToReturnItems) {
	int i;
	Customer customer;
	int clerkID;
	/*Initialization*/
	Acquire(theLocksInHomeDepotEntry._occupancyLock);
	theLocksInHomeDepotEntry._customersInStore ++;
	Release(theLocksInHomeDepotEntry._occupancyLock);
	/* initializes the shoppingList with 1..10 randomly generated items.*/
	customer._customerID = _customerID;
	customer._wantsToReturnItems = _wantsToReturnItems;
	customer._shoppingListSize = Rand(MAX_SHOPPING_LIST);
	for (i=0; i < customer._shoppingListSize; i++)
	{
		customer._shoppingList[i] = Rand(MAX_ITEMS)-1; /*pick a random item from our items catalog*/
	}
	if (customer._wantsToReturnItems==FALSE)
	{
#ifdef PRINT_OUTPUT_CUSTOMER
		printf_5("Customer ",customer._customerID ," wants to buy ",customer._shoppingListSize ," no.of items.\n");
#endif
	}
	else
	{
#ifdef PRINT_OUTPUT_CUSTOMER
		printf_5("Customer ",customer._customerID ," needs to return ",customer._shoppingListSize ," no.of items.\n");
#endif
	}
	/*Start shoppping*/
    if (customer._wantsToReturnItems == FALSE) {
        /* We just got into the store, lets collect our items */
        CollectShoppingListItems(customer._customerID, customer._wantsToReturnItems, customer._shoppingList, customer._shoppingListSize);
        /* Now all of the items have been collected. lets check out */
    }

    #ifndef IGNORE_CLERKS
        /* we need to get into the shortest line */
        /* and wait till its our turn. */
        clerkID = GetIntoTheShortestClerkLine(customer._customerID);

        /* Now its our turn. Lets provide our items to clerk */
        ProvideTheItemsToClerk(customer._customerID, customer._wantsToReturnItems, customer._shoppingList, customer._shoppingListSize, clerkID);

        /* Clerk lock is already acquired in previous step */
        /* so we are guaranteed we have the attention of clerk */
        /* lets pay the amount due or take our refund */
        TransferPayment(customer._customerID, customer._wantsToReturnItems, customer._shoppingList, customer._shoppingListSize, clerkID);

        #ifdef PRINT_LOCKS_CLERK
            printf_5("CUSTOMER #",customer._customerID," clerkLock[", clerkID,"]->Release()\n");
        #endif
        Release(theLocksInHomeDepotEntry._clerkLock[clerkID]);

        #ifdef PRINT_LOCKS_CLERK
            printf_5("CUSTOMER #",customer._customerID," clerkLock[",clerkID,"]->Release() Done!\n");
        #endif
    #endif

	#ifdef PRINT_EXTRA_LOG_CUSTOMER
	if (customer._wantsToReturnItems)
	{
        printf_3("CUSTOMER #"
				,customer._customerID
				,", Returning: Leaving the store.\n");
	}
	else
	{
		printf_3("CUSTOMER #"
					,customer._customerID
					,", Purchasing: Leaving the store.\n");
	}
    #endif
    Acquire(theLocksInHomeDepotEntry._occupancyLock);
    theLocksInHomeDepotEntry._customersInStore --;
    Release(theLocksInHomeDepotEntry._occupancyLock);
}

#endif /* USERCUSTOMER_H_ */
